﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;

namespace BitcoinLibrary
{
    public class StoredBlock /*: Serializable */
    {
        private static readonly long serialVersionUID = -6097565241243701771L;

        private Block header;
        private BigInteger chainWork;
        private int height;

        public StoredBlock(Block header, BigInteger chainWork, int height)
        {
            this.header = header;
            this.chainWork = chainWork;
            this.height = height;
        }

        /**
        * The block header this object wraps. The referenced block object must not have any transactions in it.
        */
        public Block getHeader()
        {
            return header;
        }

        /**
        * The total sum of work done in this block, and all the blocks below it in the chain. Work is a measure of how
        * many tries are needed to solve a block. If the target is set to cover 10% of the total hash value space,
        * then the work represented by a block is 10.
        */
        public BigInteger getChainWork()
        {
            return chainWork;
        }

        /**
        * Position in the chain for this block. The genesis block has a height of zero.
        */
        public int getHeight()
        {
            return height;
        }

        /** Returns true if this objects chainWork is higher than the others. */
        public bool moreWorkThan(StoredBlock other)
        {
            return chainWork.CompareTo(other.chainWork) > 0;
        }

        public override bool Equals(object other)
        {
            if (!(other is StoredBlock)) return false;
            StoredBlock o = (StoredBlock)other;
            return o.header.Equals(header) && o.chainWork.Equals(chainWork) && o.height == height;
        }

        public override int GetHashCode()
        {
            // A better hashCode is possible, but this works for now.
            return header.GetHashCode() ^ chainWork.GetHashCode() ^ height;
        }

        /**
        * Creates a new StoredBlock, calculating the additional fields by adding to the values in this block.
        */
        public StoredBlock build(Block block)
        {
            // Stored blocks track total work done in this chain, because the canonical chain is the one that represents
            // the largest amount of work done not the tallest.
            BigInteger chainWork = this.chainWork + block.getWork();
            int height = this.height + 1;
            return new StoredBlock(block.cloneAsHeader(), chainWork, height);
        }

        /**
        * Given a block store, looks up the previous block in this chain. Convenience method for doing
        * <tt>store.get(this.getHeader().getPrevBlockHash())</tt>.
        *
        * @return the previous block in the chain or null if it was not found in the store.
        */
        public StoredBlock getPrev(BlockStore store)
        {
            return store.get(getHeader().getPrevBlockHash());
        }

        public override string ToString()
        {
            return String.Format("Block {0} at height {1}: {2}",
            getHeader().getHashAsString(), getHeight(), getHeader().ToString());
        }
    }
}
